﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;

using FRL.DebtChased.Extensions;
using System.Threading;

namespace FRL.DebtChased.Repository
{

	/// <summary>
	/// Extensions to the RecentActivity model
	/// </summary>
	public partial class RecentActivity
	{

		/////////////////////////////////////////////////////////////
		// Constants

		private const int LockTimeoutSecs = 2 * 60;

		private const string MutexName = "FRL_DebtChased_RecentActivity_Entity";

		/////////////////////////////////////////////////////////////
		// Attributes

		static Mutex m_mutex = null;

		/////////////////////////////////////////////////////////////
		// Construction

		static RecentActivity()
		{

			// Create the mutex
			bool bCreatedNew;
			m_mutex = new Mutex(false, MutexName, out bCreatedNew);

		}

		/////////////////////////////////////////////////////////////
		// Static Operations

		public static string AttemptLock( DebtChasedEntities ent, string strTable, int? iPrimaryId, int? iUserId )
		{

			// Declare return variable
			string strFtmp = null;

			// Declaration of local variables
			bool bDelta = false;
			string strBlockingUserFullName = null;
			int? iBlockingUserId = 0;

			// Perform operations within a mutex scope
			if (m_mutex.WaitOne(100))
			{

				try
				{

					// Start by deleting all of the old locks
					DateTime dtExpires = DateTime.Now.AddSeconds( -1 * LockTimeoutSecs );
					string strSql = string.Format("DELETE FROM tbRecentActivity WHERE fdTimestamp < {0}", dtExpires.ToDBString());
					ent.ExecuteStoreCommand(strSql);

					// Load the most recent activity for the table
					List<RecentActivity> results = (from ra in ent.RecentActivities
												   where (ra.TableName == strTable)
												   orderby ra.Timestamp descending
												   select ra).ToList();

					// If there are no locks...
					if (results.Count == 0)
					{

						// Can lock by adding record
						bDelta = true;
						RecentActivity ra = RecentActivity.CreateRecentActivity(0, strTable, DateTime.Now);
						ra.UserId = iUserId;
						ra.PrimaryId = iPrimaryId;
						ent.RecentActivities.AddObject(ra);

					}
					else
					{

						// Find the blocking lock
						if (iPrimaryId == null)
						{

							// Loop through activity
							foreach (RecentActivity ra in results)
							{

								// If this is NOT our id...
								if (ra.UserId != iUserId)
								{
									iBlockingUserId = ra.UserId;
									if(ra.UserId != null)
										strBlockingUserFullName = User.GetFullName(ent,ra.UserId.Value);
									break;
								}

							} // foreach-loop

							// If nothing blocking...
							if (iBlockingUserId == 0)
							{

								// Remove all activity, and update last record to ours
								bDelta = true;
								while (results.Count > 1)
								{
									ent.DeleteObject(results[1]);
									results.RemoveAt(1);
								}
								results[0].PrimaryId = iPrimaryId;
								results[0].UserId = iUserId;
								results[0].Timestamp = DateTime.Now;

							}

						}
						else
						{

							// Loop through activity, find blocking, find our record
							RecentActivity ourActivity = null;
							foreach (RecentActivity ra in results)
							{

								// If a blocking record...
								if (
									(ra.UserId != iUserId)
									&&
									(
										(ra.PrimaryId == iPrimaryId)
										||
										(ra.PrimaryId == null)
									)
								)
								{
									if(iBlockingUserId == 0)
									{
										iBlockingUserId = ra.UserId;
										if(ra.UserId != null)
											strBlockingUserFullName = User.GetFullName(ent, ra.UserId.Value);
									}
								}

								// If our record...
								if (
									(ra.UserId == iUserId)
									&&
									(
										(ra.PrimaryId == iPrimaryId)
									)
								)
									ourActivity = ra;

								// Break when both found
								if ((ourActivity != null) && (iBlockingUserId != 0))
									break;

							} // foreach-loop

							// If nothing blocking...
							if (iBlockingUserId == 0)
							{

								// If we already have an activity to update...
								if (ourActivity != null)
								{
									bDelta = true;
									ourActivity.Timestamp = DateTime.Now;
								}
								else
								{
									bDelta = true;
									RecentActivity ra = RecentActivity.CreateRecentActivity(0, strTable, DateTime.Now);
									ra.UserId = iUserId;
									ra.PrimaryId = iPrimaryId;
									ent.RecentActivities.AddObject(ra);
								}

							}

						}

						// If there is a block...
						if(iBlockingUserId != 0)
						{

							// Update the return variable
							if (iBlockingUserId == null)
								strFtmp = "The data is currently being used by the application.  Please try again.";
							else
								strFtmp = string.Format("The data is currently being used by {0}.  Please try again.",
									strBlockingUserFullName
								);

						}

					}

					// Submit changes
					if (bDelta)
						ent.SaveChanges();

				}
				catch (Exception x)
				{

					// Update return variable
					strFtmp = string.Format("There was an application error.  {0}",Definitions.SeeErrorLog);

					// Log the error
					ErrorLog.Add(ent, "Locking record", x, iUserId);

				}

				// Unlock mutex
				m_mutex.ReleaseMutex();

			}
			else
				strFtmp = "The record monitoring function is busy.  Please try again.";

			// Return variable
			return strFtmp;

		}

	}

}
